package com.rfid.sockethelper;



import android.content.Context;
import android.net.Uri;

import com.rfid.DirectoryBean;
import com.rfid.event.SendStatus;

import org.greenrobot.eventbus.EventBus;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

import static java.util.concurrent.locks.ReentrantReadWriteLock.*;


public class ServerSocketHelper extends SocketBase {

    private ServerSocket serverSocket = null;

    private boolean isQuitReadClient;
    Map<String,ClientSocket> mClients = new HashMap<>();

    @Override
    public void clear() {
        super.clear();
        isQuitReadClient = true;
        for (ClientSocket clientSocket : mClients.values()) {
            clientSocket.close();
        }
        mClients.clear();
        IOHelper.closeServerSocket(serverSocket);
    }

    @Override
    protected void sendFileListInfo(String ip, DirectoryBean bean) {
        mClients.get(ip).sendFileListInfo(bean);
    }

    @Override
    protected void sendFile(final String ip, final String path) {
        mExecutor.execute(new Runnable() {
            @Override
            public void run() {
                mClients.get(ip).sendFile(path);
            }
        });
    }

    public void sendFile(final Context context, final String ip, final Uri path) {
        mExecutor.execute(new Runnable() {
            @Override
            public void run() {
                mClients.get(ip).sendFile(context,path);
            }
        });
    }

    public ServerSocketHelper(OnReceiveListener onReceiveListener) {
        super(onReceiveListener);
        mExecutor.execute(new Runnable() {
            @Override
            public void run() {
                initSocket();
            }
        });
    }

    private void initSocket() {
        try {
            serverSocket = new ServerSocket(SERVER_PORT);
            startHeartCheck();
            while (!isQuitReadClient) {
                Socket socket = serverSocket.accept();
                if (isQuitReadClient) {
                    return;
                }
                ClientSocket clientSocket = new ClientSocket(socket);
                mClients.put(socket.getInetAddress().getHostAddress(),clientSocket);
                receive(clientSocket);
                postConnectStatus(clientSocket.ip,true);
//                String text = "device -> " + PHONE_INFO;
//                clientSocket.send(text);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void receive(final ClientSocket socket) {
        mExecutor.execute(new Runnable() {
            @Override
            public void run() {
                try {
                    socket.receive();
                } catch (Exception e) {
                    e.printStackTrace();
                    ClientSocket clientSocket = mClients.remove(socket.ip);
                    if (clientSocket != null) {
                        clientSocket.close();
                    }
                }
            }
        });
    }

    private void startHeartCheck() {
        mExecutor.execute(new Runnable() {

            @Override
            public void run() {
                while (!isQuitReadClient) {
                    try {
                        Thread.sleep(5000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("client size = " + mClients.size());
                    for (ClientSocket socket : mClients.values()) {
                        try {
                            System.out.println("send heart" + socket.ip);
                            socket.sendHeart();
                        } catch (Exception e) {
                            e.printStackTrace();
                            socket.close();
                            mClients.remove(socket.ip);
                            break;
                        }
                    }
                }
            }
        });
    }

    public void send(final String text) {
        if (mClients.size() == 0) {
            return;
        }
        mExecutor.execute(new Runnable() {
            @Override
            public void run() {
                for (ClientSocket clientSocket:mClients.values()) {
                    clientSocket.send(text);
                }
            }
        });
    }

    public void sendConfig(final String ip, final String name, final String text) {
        if (mClients.size() == 0) {
            return;
        }
        mExecutor.execute(new Runnable() {
            @Override
            public void run() {
                ClientSocket socket = mClients.get(ip);
                if (socket != null) {
                    socket.sendConfig(name,text);
                }
            }
        });
    }

    public void sendConfigNames(final String ip, final String text) {
        if (mClients.size() == 0) {
            return;
        }
        mExecutor.execute(new Runnable() {
            @Override
            public void run() {
                ClientSocket socket = mClients.get(ip);
                if (socket != null) {
                    socket.sendConfigNames(text);
                }
            }
        });
    }

    public void sendQueryFileListCmd(final String ip, final String path) {
        if (mClients.size() == 0) {
            return;
        }
        System.out.println("sendQueryFileListCmd");
        mExecutor.execute(new Runnable() {
            @Override
            public void run() {
                ClientSocket socket = mClients.get(ip);
                if (socket != null) {
                    socket.sendQueryFileListCmd(path);
                }
            }
        });
    }

    public void sendReqFileCmd(final String ip, final String path) {
        if (mClients.size() == 0) {
            return;
        }
        System.out.println("sendQueryFileListCmd");
        mExecutor.execute(new Runnable() {
            @Override
            public void run() {
                ClientSocket socket = mClients.get(ip);
                if (socket != null) {
                    socket.sendReqFileCmd(path);
                }
            }
        });
    }

    public class ClientSocket extends Base{
        private Socket socket = null;
        private InputStream inputStream = null;
        private OutputStream outputStream = null;
        private boolean isQuitReadMessage;
        private String ip;
        private Lock mLock;

        public ClientSocket(Socket socket) throws IOException {
            this.socket = socket;
            inputStream = socket.getInputStream();
            outputStream = socket.getOutputStream();
            ip = socket.getInetAddress().getHostAddress();
            mLock = new ReentrantLock();
            init(inputStream,outputStream,ip,mLock);
        }

        public void close() {
            isQuitReadMessage = true;
            IOHelper.closeOutputStream(outputStream);
            IOHelper.closeInputStream(inputStream);
            IOHelper.closeSocket(socket);
        }

        public void receive() throws Exception {
            while (!isQuitReadMessage) {
                Thread.sleep(500);
                short cmd = IOHelper.readCmd(inputStream);
                String text = ServerSocketHelper.this.receive(inputStream,ip,cmd);
                if (text == null || text.length() == 0) {
                    continue;
                }
                postToUI(ip,cmd,text);
            }
        }

        public void send(String text) {
            try {
                mLock.lock();
                IOHelper.writeText(outputStream, text);
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                mLock.unlock();
            }
        }
        public void sendConfig(String name,String text) {
            try {
                mLock.lock();
                IOHelper.writeConfig(outputStream,name,text);
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                mLock.unlock();
            }
        }

        public void sendConfigNames(String text) {
            try {
                mLock.lock();
                IOHelper.sendConfigNames(outputStream,text);
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                mLock.unlock();
            }
        }

        public void sendHeart() throws IOException {
            try {
                mLock.lock();
                IOHelper.sendHeart(outputStream);
            } finally {
                mLock.unlock();
            }
        }

        public void sendFile(final Context context, final Uri uri) {
            try {
                mLock.lock();
                super.sendFile(outputStream, context, uri, ip);
            } finally {
                mLock.unlock();
            }
        }

        public void sendFileListInfo(DirectoryBean bean) {
            try {
                mLock.lock();
                super.sendFileList(outputStream, bean, ip);
            } finally {
                mLock.unlock();
            }
        }


        public void sendFileToPath(String path,String rpath) {
            try {
                mLock.lock();
                super.sendFileToPath(outputStream, path, rpath,ip);
            } finally {
                mLock.unlock();
            }
        }
    }
}
